package martin.tpfinal.grabar.grafico;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;
import javax.swing.Timer;

public class TestData {

	private Capturar captura;
	private TargetDataLine line;
	private VentanaPrincipal ventana;
	
	public TestData(Capturar captura, VentanaPrincipal ventana){
		this.captura = captura;
		this.ventana = ventana;
		correr();
	}
	
	
    public void correr() {

    	AudioFormat format = ventana.getFormatControls().getFormat();
         
        DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
              
        System.out.println(format.toString());
//        System.out.println(info.toString());
        
        if (!AudioSystem.isLineSupported(info)) {
            captura.shutDown("Line matching " + info + " not supported.");
            return;
        }

        // get and open the target data line for capture.
        try {
            line = (TargetDataLine) AudioSystem.getLine(info);
            line.open(format, line.getBufferSize());
        } catch (Exception ex) { 
        	captura.shutDown(ex.toString());
            return;
        }

        // play back the captured audio data
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int frameSizeInBytes = format.getFrameSize();
        int bufferLengthInFrames = line.getBufferSize() / 8;
        int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
        byte[] data = new byte[bufferLengthInBytes];
        int numBytesRead;
        
        //Si uso 11025 HZ, con 8bits (1 byte), entonces frameRate = 11025 y framezise es 1, si los multiplico me da el numero de bytes que leera en 1 seg
        //entonces elijo la cantidad que voy a ir capturando
        
        /*                       bytes               bytes 
         * 11025 / 63 = 175    ( 11025 -> 1 seg  ==>  175 -> 0.0158 seg)
         * 11025 / 45 = 245    ( 11025 -> 1 seg  ==>  245 -> 0.022 seg)
         * 11025 / 35 = 315    ( 11025 -> 1 seg  ==>  315 -> 0.028 seg)
         * 11025 / 75 = 147    ( 11025 -> 1 seg  ==>  147 -> 0.013 seg)
         *
         * 					   ( 6615 -> 0.6 seg   - usando el de 245 tengo que tomar 27 muestras   (0.6 / 0.022) o (27 * 245)
         *                                         - usando el de 315 tengo que tomar 21 muestras   
         *                                         - usando el de 175 tengo que tomar 38 muestras
         *                                         - usando el de 147 tengo que tomar 45 muestras
         *
         * 0.6 es el tiempo que grabo para capturar las palabras 
         *                                         
         *                     ( 7718 -> 0.7 seg   - usando el de 147 tengo que tomar 52 muestras
         *
         * 0.7 es el tiempo que grabo para capturar las palabras                                        
         * */
        
        int divParaObtenerdiezmil=75;
        int muestras = 52;
        
        int numBytesCadaDiezMili = (int)((format.getFrameRate()*format.getFrameSize())/divParaObtenerdiezmil);
        byte[] data2 = new byte[numBytesCadaDiezMili];
        
        Timer timer=createTmmer();
        line.start();
        	           	            
//        timer.start();
        
        int[] silence=new int[20];
        int[] energy=new int[20];
        int indice=0;
        double cero=0;
        double energia=0;
        int counter=0;
        boolean esperando=true;
        
        //voy a suponer que mis palabras deben durar 0.65 segundos, serian 650 milisegundos
        //entonces cuando detecto que es una palabra, debo grabar 65 veces del read
        while (captura.getThread() != null) {

            if((numBytesRead = line.read(data2, 0, numBytesCadaDiezMili)) == -1) {
                break;
            }
            
            /*
             * Grabando en calidad estudio
             * Volumen mic 70
             * potencia +30
             */
            
            processData(numBytesRead,data2);
            
            if (esperando){
            	System.out.println("Esta esperando***************");
                int e=getEnergy(numBytesRead, data2);
                int ZCR=getZeroCrosingRateWithoutCero(numBytesRead, data2);
                if (e > 100 && ZCR > 5 ){  // estaba en 100 y 10
                	out = new ByteArrayOutputStream();
                	out.write(data2, 0, numBytesRead);
                	counter++;
                	esperando=false;
                }
            }
            else{
                out.write(data2, 0, numBytesRead);
                counter++;
            }
            
            if (counter == muestras){
            	procesarYenviarpalabra(out,format,frameSizeInBytes);
            	counter=0;
            	esperando=true;
        	}
        }
        timer.stop();

        // we reached the end of the stream.  stop and close the line.
        line.stop();
        line.close();
        line = null;

        System.out.println(("Totales potencia: "+mayorPot));
        System.out.println(("Totales cero: "+mayorCero));
        
        // stop and close the output stream
        try {
            out.flush();
            out.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }

    private void procesarYenviarpalabra(ByteArrayOutputStream out,AudioFormat format,int frameSizeInBytes) {
        // stop and close the output stream
        try {
            out.flush();
            out.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        // load bytes into the audio input stream for playback
        byte audioBytes[] = out.toByteArray();
        ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes);
        AudioInputStream audioFile=new AudioInputStream(bais, format, audioBytes.length / frameSizeInBytes);
        ventana.setAudioInputStream( audioFile);

        long milliseconds = (long)((ventana.getAudioInputStream().getFrameLength() * 1000) / format.getFrameRate());
        ventana.setDuration( milliseconds / 1000.0);

        try {
        	ventana.getAudioInputStream().reset();
        } catch (Exception ex) { 
            ex.printStackTrace(); 
            return;
        }

        ventana.getSamplingGraph().createWaveForm(audioBytes);
        
        String fileName="C:\\Users\\Martin\\Desktop\\sonidos\\natalia\\sonidos ZCR 5, energia 100\\otros"+index++ +".wav";
        ventana.saveToFile(fileName, AudioFileFormat.Type.WAVE);
        
        System.out.println(fileName);


	}

    int index=1;
	
	int numProceso=0;
	private void processData(int numBytesRead, byte[] data) {
        int max=0;
        int min=0;
        int actual=0;
        System.out.println("-----------------------"+ numProceso++ +"-----------------------------");
//        System.out.println("leyo: "+numBytesRead);
		for (int i = 0; i < numBytesRead; i++) {
			actual=data[i];
//			System.out.print(actual+" ; ");
			if (max<actual)
				max=actual;

			if (min>actual)
				min=actual;

		}
//		System.out.println();
		System.out.println("El maximo es : "+max+"; El minimo es :"+min);
//		int e = getEnergy(numBytesRead, data);
//		if (e>15000){ System.out.println("********************* MAYOR A 15000"); mayorPot++;}
//		Integer CERO = getZeroCrosingRateWithoutCero(numBytesRead, data);
//		if (CERO>29){ System.out.println("********************** MAYOR A 29"); mayorCero++;}

	}

	static int mayorPot=0;
	static int mayorCero=0;
    
	private int getEnergy(int numBytesRead, byte[] data) {
        int sum=0;
        for (int i = 0; i < numBytesRead; i++) {
			sum+=Math.abs(data[i]);
		}
		System.out.println("La enegia es: "+sum);
		return sum;
	}



	private int getZeroCrosingRateWithoutCero(int numBytesRead, byte[] data) {
        int sum=0;

        int prevValue=data[0]; 
        
        for (int i = 1; i < numBytesRead-1; i++) {
			
        	if (data[i]!=0){
            	if (data[i]>0 && prevValue<0 && data[i+1]>0) 
            	   	sum++;
				if (data[i]<0 && prevValue>0 && data[i+1]<0) 
            	   	sum++;
				prevValue=data[i];
        	}
        }
		System.out.println("Cruza el 0 : "+sum);
		return sum;
	}

	private int getZeroCrosingRateWithoutCeroOld(int numBytesRead, byte[] data) {
        int sum=0;

        int prevValue=data[0]; 
        
        for (int i = 1; i < numBytesRead; i++) {
			
        	if (data[i]!=0){
            	if (data[i]>0 && prevValue<0) 
            	   	sum++;
				if (data[i]<0 && prevValue>0) 
            	   	sum++;
				prevValue=data[i];
        	}
        }
		System.out.println("Cruza el 0 : "+sum);
		return sum;
	}

	/*
	 * Se calcula sobre 100 mseg, en nuestro caso 10 periodos, ya que 1 periodo -> 10 mseg
	 * 
	 */
	private double getEnergyPaper(int[] data) {
        //valor umbral de energia

		//el pico de energia maximo del silencio
		double IMX=0;
		//la energia promedio del silencio
		double IMN=0;
			
		//el limite inferior del umbral
		double ITL=0;
		//el limite superior del umbral
		double ITU=0;
		
		IMX=data[0];
		for (int i = 1; i < data.length; i++) {
			if (data[i]>IMX)
				IMX=data[i];
		}
		IMN=media(data);
		double desv=desviacionEstandar(data);
		System.out.println("Maxima: "+IMX+", media: "+IMN+", desviacion: "+desv);
		
		double I1=0.03*(IMX-IMN)+IMN;
		double I2=4*IMN;
		ITL=Math.min(I1,I2);
		ITU=5*ITL;
		System.out.println("ENER      ITL : "+ITL+", ITU: "+ITU);
		
		//probar con 1.5*IMX o la media mas 2 veces la desviacion (estuve usando 10000)
		double enegia=Math.max(1.5*IMX,IMN+2*desv);//,2000);
		
		System.out.println("ENER            1.5*IMX: "+1.5*IMX+ "|||| IMN+2*desv: "+(IMN+2*desv));
		return enegia;	
		
	}
	
	
	/*
	 * Se calcula sobre 100 mseg, en nuestro caso 10 periodos, ya que 1 periodo -> 10 mseg
	 * 
	 */
	private double getZeroCrosingRatePaper(int[] data) {
        //valor umbral de cruces de cero
		//es el minimo entre un valor umbral fijo IF y (la suma del valor medio + la desviacion estandar)
		// IZCT = MIN ( IF , IZC + 2G(IZC) )
		double IZCT=0;
			
		//umbral fijo	//125
		double IF=	80;
		
		//media de la taza de cruces de cero durante el silencio 
		//(sumo todos los calculos que hice en los distintos periodos y los dividos por la cantidad que son, es un promedio comun)
		double media=media(data);
		double desvStandar=desviacionEstandar(data);
		System.out.println("CERO Media: "+media+", desviacion: "+desvStandar);
		double IZC_final = media(data)+(2*desviacionEstandar(data));
		System.out.println("CERO IZC : "+IZC_final);
		IZCT=Math.max(IF, IZC_final);
		//
		return IZCT;	
		
	}
	
	
	public double media(int val[]){
		int suma=0;
		for (int i = 0; i < val.length; i++) {
			suma+= val[i];
		}
		
		return suma/val.length;
	}
	
	public double desviacionEstandar(int arreglo[])  {
		double suma = 0;
		double media = media(arreglo);
		double desviacion = 0;
		double diferencia = 0;
		
		for(int i = 0; i < arreglo.length; i++) {
			diferencia = (arreglo[i] - media);
			desviacion += Math.pow(diferencia, 2);
		}
		desviacion = desviacion/arreglo.length;
		return Math.sqrt(desviacion);
		
	}

	
    int tiempo=1;
	private Timer createTmmer() {
		tiempo=0;
		Timer timer = new Timer (1000, new ActionListener ()
	    {
	        public void actionPerformed(ActionEvent e)
	        {
	        	System.out.println();
	            System.out.println("***********Paso el segundo: "+tiempo++);
	            System.out.println();
	         }
	    });
	    			    
		return timer;
	}


	
}
